package cheme.ProcessEditor.Parts;

import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.CompoundSnapToHelper;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.SnapToGeometry;
import org.eclipse.gef.SnapToGrid;
import org.eclipse.gef.SnapToGuides;
import org.eclipse.gef.SnapToHelper;
import org.eclipse.gef.rulers.RulerProvider;

import cheme.ProcessEditor.Figures.SubprocessFig;
import cheme.ProcessEditor.Policies.ChemeSnapFeedbackPolicy;
import cheme.ProcessEditor.Policies.ChildRegroupPolicy;
import cheme.ProcessEditor.Policies.ImplicitTerminalNodePolicy;
import cheme.ProcessEditor.Policies.ProcessModelLayoutPolicy;
import cheme.ProcessEditor.Policies.SubprocessComponentPolicy;
import cheme.ProcessModel.Subprocess.Subprocess;


public class SubprocessPart extends PropertyAwarePart
{
	private XYLayout xYLayout;
	
	protected IFigure createFigure()
	{
		SubprocessFig figure = new SubprocessFig();
		xYLayout = new XYLayout();
		
		figure.setLayoutManager(xYLayout);

		return figure;
	}
	
	private Subprocess getSubprocess()
	{
		return (Subprocess)getModel();
	}
	
	private SubprocessFig getProcessUnitFig()
	{
		return (SubprocessFig)getFigure();
	}

	@Override
	protected void createEditPolicies()
	{
		installEditPolicy(EditPolicy.COMPONENT_ROLE, new SubprocessComponentPolicy(xYLayout));
		installEditPolicy(EditPolicy.CONTAINER_ROLE, new ChildRegroupPolicy()); //xYLayout
		installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new ImplicitTerminalNodePolicy());
		installEditPolicy(EditPolicy.LAYOUT_ROLE, new ProcessModelLayoutPolicy(xYLayout));
		
		/*
		 * Install the snap feedback policy:
		 * An EditPolicy that is used to show snap feedback for guides (red 
		 * lines) and geometry (blue lines). This policy is not required for 
		 * Grids.
		 */
		installEditPolicy("Snap Feedback", new ChemeSnapFeedbackPolicy());
	}
	
	protected List getModelChildren()
	{
		List children = new ArrayList();
		children.addAll(getSubprocess().getChildren());
		return children;
	}
	
	@Override
	protected void refreshVisuals()
	{
		Rectangle constraint = getSubprocess().getBounds();
//		getProcessUnitFig().setText(constraint.getTopLeft().toString());
		((GraphicalEditPart)getParent()).setLayoutConstraint(this, getFigure(), constraint);
	}
	
	@Override
	protected void handleBoundsChange(PropertyChangeEvent evt)
	{
		refreshVisuals();
		super.handleBoundsChange(evt);
	}
	
	/**
	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
	 */
	public Object getAdapter(Class adapter)
	{
		/*------------------------------------------------------------------
		 * Adapt this edit part into a CompoundSnapToHelper. This class combines 
		 * multiple SnapToHelpers into one compound helper.
		 * 
		 * A helper used temporarily by Tools for snapping certain mouse 
		 * interactions. SnapToHelpers should not be reused by tools or by the 
		 * editparts which provide them to the tools. For example, for a move 
		 * operation, the life-cycle of a SnapToHelper begins when a drag is 
		 * initiated, and ends when the drag is over. If another drag is 
		 * initiated right after the first one is completed, new SnapToHelpers 
		 * are employed. This means that helpers can assume that everything else 
		 * is static, and there is no need to track changes outside of the helper.
		 * 
		 * We actually use one of three derived objects here:
		 * SnapToGuides, SnapToGrid, or SnapToGeometry.
		 * 
		 */
		if (adapter == SnapToHelper.class)
		{
			List<SnapToHelper> snapStrategies = new ArrayList<SnapToHelper>();
			
			/*------------------------------------------------------------------
			 * There are three readymade snap to references that the GEF provides,
			 * these are: guides/rulers, a background grid, and the geometry of 
			 * sibling figures/editpsrts (which is it specifically?).
			 * 
			 * For all of these snap refrences, the graphical viewer has a
			 * proprty that must be set to true for the snapping attempt to be
			 * initiated here. 
			 */
			
			/*------------------------------------------------------------------
			 * Are we to snap to a guide?
			 * 
			 * SnapToGuides is a helper used to perform snapping to guides. The 
			 * guides are obtained from the viewer's horizontal and vertical 
			 * RulerProviders. If snapping is performed, the request's extended 
			 * data will contain keyed values indicating which guides were 
			 * snapped to, and which side of the part should be attached. 
			 * Generally snapping to a guide should attach the part to that guide, 
			 * but application behavior may differ.
			 * 
			 * Snapping (and attaching) to a guide is only possible if a single 
			 * part is being dragged. The current implementation will not snap if 
			 * a request contains multiple parts. This may be relaxed in the 
			 * future to allow snapping, but without setting the attachment 
			 * extended data.
			 * 
			 * This helper does not keep up with changes in guides. Clients should 
			 * instantiate a new helper each time one is requested and not hold 
			 * on to instances of the helper. 
			 */
			Boolean snapCriterion = (Boolean) getViewer().getProperty(RulerProvider.PROPERTY_RULER_VISIBILITY);
			if (snapCriterion != null && snapCriterion.booleanValue())
			{	
				snapStrategies.add(new SnapToGuides(this));
			}

			/*------------------------------------------------------------------
			 * Are we to snap to the background grid?
			 * 
			 * SnapToGeometry is a temporary helper used to perform snapping to 
			 * existing elements. This helper can be used in conjunction with the 
			 * DragEditPartsTracker when dragging editparts within a graphical 
			 * viewer. Snapping is based on the existing children of a container. 
			 * When snapping a rectangle, the edges of the rectangle will snap to 
			 * edges of other rectangles generated from the children of the given 
			 * container. Similarly, the centers and middles of rectangles will 
			 * snap to each other.
			 * 
			 * If the snap request is being made during a Move, Reparent or 
			 * Resize, then the figures of the participants of that request will 
			 * not be used for snapping. If the request is a Clone, then the 
			 * figures for the parts being cloned will be used as possible snap 
			 * locations.
			 * 
			 * This helper does not keep up with changes made to the container 
			 * editpart. Clients should instantiate a new helper each time one 
			 * is requested and not hold on to instances of the helper. 
			 */
			snapCriterion = (Boolean) getViewer().getProperty(SnapToGrid.PROPERTY_GRID_ENABLED);
			if (snapCriterion != null && snapCriterion.booleanValue())
			{
				snapStrategies.add(new SnapToGrid(this));
			}

			/*------------------------------------------------------------------
			 * Finally, are we to snap to the geometry of sibling 
			 * editparts/figures?
			 * 
			 * SnapToGrid is a helper used to perform snapping to a grid, which 
			 * is specified on the graphical viewer via the various properties 
			 * defined in this class. This helper can be used in conjunction with 
			 * the DragEditPartsTracker when dragging editparts within a graphical 
			 * viewer. When snapping a rectangle, the edges of the rectangle will 
			 * snap along gridlines.
			 * 
			 * This helper does not keep up with changes made to the graphical 
			 * viewer's properties. Clients should instantiate a new helper each 
			 * time one is requested and not hold on to instances of the helper, 
			 * if the grid properties specified on the viewer are subject to 
			 * change.
			 */
			snapCriterion = (Boolean) getViewer().getProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED);
			if (snapCriterion != null && snapCriterion.booleanValue())
			{
				snapStrategies.add(new SnapToGeometry(this));
			}
			
			if (snapStrategies.size() == 0)
			{
				return null;
			}
			
			if (snapStrategies.size() == 1)
			{
				return snapStrategies.get(0);
			}
			
			SnapToHelper ss[] = new SnapToHelper[snapStrategies.size()];
			
			for (int i = 0; i < snapStrategies.size(); i++)
			{
				ss[i] = (SnapToHelper)snapStrategies.get(i);
			}

			return new CompoundSnapToHelper(ss);
		}
		return super.getAdapter(adapter);
	}
}
